package db

import (
	"encoding/binary"
	"errors"
	"hash"
	"os"
	"sync"
	"sync/atomic"

	"gitee.com/wheat-os/wheat-db/example/tools"
)

// 索引的描述部分信息
type DbInfo struct {
	Offset int64
	Key    string
}

func (d *DbInfo) SetOffset(offset int64) {
	atomic.SwapInt64(&d.Offset, offset)
}

// 懒树节点
type TreeNode struct {
	Foliage   [10]*TreeNode // 孩子节点
	Depth     uint64        // 当前树的深度
	HomeInfos []*DbInfo     // 家节点
	TemInfo   *DbInfo       // 临时位置
	mu        sync.Mutex
}

type LazyTreeIndex struct {
	Root *TreeNode

	f *os.File // 索引储存地点

	hashFunc hash.Hash

	hashMu sync.Mutex
}

// 计算 hash
func (l *LazyTreeIndex) sumHashKey(key string) uint64 {
	l.hashMu.Lock()

	l.hashFunc.Reset()
	l.hashFunc.Write([]byte(key))

	hashSum := l.hashFunc.Sum(nil)
	l.hashMu.Unlock()
	hashKey := make([]byte, 8)
	copy(hashKey, hashSum)

	return binary.BigEndian.Uint64(hashKey)
}

func (l *LazyTreeIndex) getOrNewMove(node *TreeNode, point uint64) *TreeNode {
	if node.Foliage[point] != nil {
		return node.Foliage[point]
	}

	newNode := &TreeNode{
		Depth: node.Depth + 1,
	}
	node.Foliage[point] = newNode
	return newNode
}

// 驱动节点
func (l *LazyTreeIndex) expelled(node *TreeNode, info *DbInfo) {

	node.mu.Lock()
	defer node.mu.Unlock()

	hashSum := l.sumHashKey(info.Key)
	homeDepth := tools.LengthUInt64(hashSum)

	// 是否到达 Home
	if node.Depth == homeDepth {
		oldInfo := l.findInfoByHome(node.HomeInfos, info.Key)
		if oldInfo != nil {
			oldInfo.SetOffset(info.Offset)
			return
		}

		node.HomeInfos = append(node.HomeInfos, info)
		return
	}

	// 发生懒惰
	if node.TemInfo == nil {
		node.TemInfo = info
		return
	}

	// 替换
	if node.TemInfo.Key == info.Key {
		node.TemInfo.SetOffset(info.Offset)
		return
	}

	// 驱逐单前节点的 info
	hashSum = l.sumHashKey(node.TemInfo.Key)

	hashSum /= tools.PowUInt64(10, node.Depth)
	nextPoint := hashSum % 10

	nextNode := l.getOrNewMove(node, nextPoint)

	// 发生驱逐
	l.expelled(nextNode, node.TemInfo)
	node.TemInfo = info
}

// 添加 一个 Db 的 Info 描述节点
func (l *LazyTreeIndex) Insert(info *DbInfo) {
	if info == nil {
		return
	}

	hashSum := l.sumHashKey(info.Key)
	nextPoint := hashSum % 10

	node := l.Root.Foliage[nextPoint]

	// 进行 懒惰 以及 驱逐
	l.expelled(node, info)
}

func (l *LazyTreeIndex) findInfoByHome(h []*DbInfo, key string) *DbInfo {
	for _, v := range h {
		if v.Key == key {
			return v
		}
	}

	return nil
}

func (l *LazyTreeIndex) Update(d *DbInfo) error {
	if d == nil {
		return errors.New("db info can't be empty")
	}

	info := l.Find(d.Key)
	if info == nil {
		return errors.New("key was not found")
	}

	info.SetOffset(d.Offset)
	return nil
}

func (l *LazyTreeIndex) Find(key string) *DbInfo {
	hashSum := l.sumHashKey(key)
	if hashSum == 0 {
		return l.findInfoByHome(l.Root.Foliage[0].HomeInfos, key)
	}

	node := l.Root
	for hashSum != 0 {
		if node == nil {
			return nil
		}

		node.mu.Lock()

		if node.TemInfo != nil && node.TemInfo.Key == key {
			node.mu.Unlock()
			return node.TemInfo
		}

		nexPont := hashSum % 10
		nodeNext := node.Foliage[nexPont]
		node.mu.Unlock()

		node = nodeNext
		hashSum /= 10
	}

	if node == nil {
		return nil
	}

	// 检查是否到家
	node.mu.Lock()
	defer node.mu.Unlock()

	return l.findInfoByHome(node.HomeInfos, key)
}

func NewLazyTreeIndex(hashFunc hash.Hash) *LazyTreeIndex {
	root := new(TreeNode)
	root.Depth = 0
	root.HomeInfos = nil
	root.TemInfo = nil

	for i := 0; i < 10; i++ {
		root.Foliage[i] = &TreeNode{
			Depth: 1,
		}
	}

	return &LazyTreeIndex{Root: root, f: nil, hashFunc: hashFunc}
}
